package com.seer.srd.qingdao

import com.seer.srd.BusinessError
import com.seer.srd.db.MongoDBManager
import com.seer.srd.robottask.*
import com.seer.srd.robottask.component.TaskComponentDef
import com.seer.srd.robottask.component.TaskComponentParam
import com.seer.srd.robottask.component.parseComponentParamValue
import com.seer.srd.roboview.*
import com.seer.srd.storesite.StoreSiteService
import com.seer.srd.util.mapper
import org.litote.kmongo.*

import org.slf4j.LoggerFactory

object ExtraComponent {

    private val logger = LoggerFactory.getLogger(ExtraComponent::class.java)

    private var warnMark = false

    val extraComponents: List<TaskComponentDef> = listOf(
        TaskComponentDef(
            "extra", "CheckTaskToSiteExisted", "检查任务的终点是否已经存在，如果没有，则先选择", "",
            false, listOf(
            ), false
        ) { _, ctx ->
            val task = ctx.task
            val fromSiteId = task.persistedVariables[PvsFields.FromSiteId]?.toString()
            if (fromSiteId.isNullOrBlank()) throw BusinessError("当前任务还未被指定起点，请求撤销此任务！")
            var toSiteId = task.persistedVariables[PvsFields.ToSiteId]?.toString()
            if (!toSiteId.isNullOrBlank()) return@TaskComponentDef
            //todo: findToSiteFrom WB area
            val split = fromSiteId.split("-")[1]
            val list1 = NewTaskListener.findToSiteIdsFromWBGallery("WB-$split-1")
            val list2 = NewTaskListener.findToSiteIdsFromWBGallery("WB-$split-2")
            if (list1.isEmpty() && list2.isEmpty()) throw BusinessError("当前任务终点库位不足，请尽快入库余留足够终点库位！")
            toSiteId = if (list1.size > list2.size) list1[0] else list2[0]
            task.persistedVariables[PvsFields.ToSiteId] = toSiteId
            val taskDef = getRobotTaskDef(task.def)!!
            val stageRefNameIndex = NewTaskListener.getStageRefNameAndIndexFromTaskDef(taskDef)
            stageRefNameIndex.forEach { (stageRefName, index) ->
                if (stageRefName.split("_").first() != StageRefNamePrefix.To) return@forEach
                task.transports[index.tIndex].stages[index.sIndex].location = toSiteId
            }
            MongoDBManager.collection<RobotTask>().updateOne(
                RobotTask::id eq task.id, set(
                    RobotTask::persistedVariables setTo task.persistedVariables
                )
            )
        },

        TaskComponentDef(
            "extra", "CheckSiteContent", "判断是否已经获取到库位上的货物详情", "",
            false, listOf(
        ), false
        ) { _, ctx ->
            val fromSiteId = ctx.task.persistedVariables[PvsFields.FromSiteId] as? String
                    ?: throw BusinessError("任务异常，缺少起点库位ID")
            val fromSite = StoreSiteService.getStoreSiteById(fromSiteId)
                    ?: throw BusinessError("系统中不存在库位【$fromSiteId】")
            if (fromSite.content == "[]"){
                throw BusinessError("库位【$fromSiteId】货物详情视觉还没识别完")
            }
            ctx.task.persistedVariables[PvsFields.SiteContent] = fromSite.content
            // todo: 更新数据库数据
            MongoDBManager.collection<RobotTask>().updateOne(RobotTask::id eq ctx.task.id, RobotTask::persistedVariables setTo ctx.task.persistedVariables)
        },

        TaskComponentDef(
            "extra", "ProcessTasksInOrder", "按顺序下发任务", "",
            false, listOf(
            ), false
        ) { _, ctx ->
            val currFrom = getFromSiteInfo(ctx.task)
            RobotTaskService.listUnEditAbleTasksCache().values.forEach {
                val from = getFromSiteInfo(it)
                if (currFrom.gallery != from.gallery) return@forEach // continue
                if (currFrom.index > from.index) {
                    // 当前任务的起点库位，不是最小编号的库位
                    if (it.transports[0].state < RobotTransportState.Success)
                        throw BusinessError("编号较小的库位上的货物还未被领走，等待...")
                    else {
                        // 编号较小的库位上的货物已经被领走了，下发...
                    }
                } else {
                    // 当前任务的起点库位，是最小编号的库位，当前任务可以被机器人执行了，下发...
                }
            }
        },

        TaskComponentDef(
            "extra", "EnableBoardCheck", "判断是否需要进行看板核对", "",
            false, listOf(
            ), true
        ) { component, ctx ->
            val enable = CustomDataService.getCustomData().enableBoardCheck;
            ctx.setRuntimeVariable(component.returnName, enable == 1)
        },

        TaskComponentDef(
            "extra", "dashboardCheckResult", "校验看板核对结果", "",
            false, listOf(
            ), true
        ) { component, ctx ->
            val watchBoardStatusList = RoboViewService.getWatchBoardStatus(CUSTOM_CONFIG.roboViewConfig.host, CUSTOM_CONFIG.roboViewConfig.port)
            var result = false
            for(i in watchBoardStatusList.indices){
                if(!watchBoardStatusList[i].storage_occupied){
                    result = true
                    CustomDataService.agvPlayAudio(CUSTOM_CONFIG.boardCheckSiteId)
                }
            }
            ctx.setRuntimeVariable(component.returnName, result)
        },

        TaskComponentDef(
            "extra", "dashboardCheckSolvedManually", "检查看板核对是否已被人为处理", "",
            false, listOf(
            ), false
        ) { component, ctx ->
            if(!CustomDataService.removeRecordIfAgvStopAudio(CUSTOM_CONFIG.boardCheckSiteId))
                throw BusinessError("库位【Check01】的看板核对状态还未进行人工校验，等待...")
            val watchBoardStatusList = RoboViewService.getWatchBoardStatus(CUSTOM_CONFIG.roboViewConfig.host, CUSTOM_CONFIG.roboViewConfig.port)
            for(i in watchBoardStatusList.indices){
                if(!watchBoardStatusList[i].storage_occupied){
                    throw BusinessError("人为处理后仍识别失败,请重新处理")
                }
            }
        },

        TaskComponentDef(
            "extra", "setSiteContent", "设置终点库位内容", "",
            false, listOf(
            TaskComponentParam("toSiteId", "终点库位ID", "string")
        ), false
        ) { component, ctx ->
            val content = ctx.task.persistedVariables[PvsFields.SiteContent] as? String
                    ?: throw BusinessError("任务异常，缺少起点库位内容")
            val toSiteId = parseComponentParamValue("toSiteId", component, ctx) as String
            StoreSiteService.setSiteContent(toSiteId, content, "设置终点库位内容")
        },

        TaskComponentDef(
            "extra", "getHeight", "取货物高度并更新层高内容", "",
            false, listOf(
            TaskComponentParam("siteId", "库位ID", "string"),
                TaskComponentParam("destination", "阶段", "string")
            ), false
        ) { component, ctx ->
            val siteId = parseComponentParamValue("siteId", component, ctx) as String
            val destination = parseComponentParamValue("destination", component, ctx) as RobotStage
            val site = StoreSiteService.getStoreSiteById(siteId)
                    ?: throw BusinessError("识别失败：SRD 不存在库位${siteId}")
            val content = site.content
            val bin_pose = mapper.readValue(content, BinPoseInfos::class.java).BinPoseInfo
            val height : Double
            if (bin_pose.size == 1) {
                height = bin_pose[0].height
                logger.debug("--------------$height")
                StoreSiteService.setSiteContent(siteId, "", "内容置为空")
                StoreSiteService.changeSiteFilled(siteId, false, "解除占用")
            } else if (bin_pose.size == 2){
                height = bin_pose[0].height
                logger.debug("--------------$height")
                val newContent = bin_pose.subList(1, bin_pose.size).json
                StoreSiteService.setSiteContent(siteId, newContent, "更新层高内容")
            } else if (bin_pose.size == 3){
                height = bin_pose[0].height
                logger.debug("--------------$height")
                val newContent = bin_pose.subList(1, bin_pose.size).json
                StoreSiteService.setSiteContent(siteId, newContent, "更新层高内容")
            } else{
                throw BusinessError("库位层高信息有误")
            }
            val base_end_height = CUSTOM_CONFIG.base_end_height
            val new_end_height = base_end_height + height
            val base_start_height = CUSTOM_CONFIG.base_start_height
            destination.operation = "ForkLoad"
            val new_start_height = base_start_height + height
            destination.properties = listOf(
                mapOf("key" to "start_height", "value" to new_start_height),
                mapOf("key" to "end_height", "value" to new_end_height),
                mapOf("key" to "recognize", "value" to "true")
            ).json
        },

        TaskComponentDef(
            "extra", "CheckTrayStatusFromPlc", "检查母托盘的状态，如果不能放货则报警(可通过 trayAvailable 获取结果)", "",
            false, listOf(
            ), false
        ) { _, ctx ->
            val task = ctx.task
            if (task.def != NewTaskListener.taskDefNameMatInStoreA && task.def != NewTaskListener.taskDefNameMatInStoreB) return@TaskComponentDef
            val toSiteId = task.persistedVariables[PvsFields.ToSiteId]?.toString()
            if (toSiteId.isNullOrBlank()) throw BusinessError("无法获取当前任务的终点库位！")
            val bigTray = toSiteId.contains("Big")
            val trayAvailable = PlcListener.checkToSiteAvailable(bigTray)
            task.persistedVariables[PvsFields.TrayAvailable] = trayAvailable
            if (!trayAvailable) CustomDataService.agvPlayAudio(toSiteId)
            MongoDBManager.collection<RobotTask>().updateOne(
                RobotTask::id eq task.id, set(
                    RobotTask::persistedVariables setTo task.persistedVariables
                )
            )
        },

        TaskComponentDef(
            "extra", "CheckTrayStatusSolvedManually", "检查母托盘是否已被人为处理", "",
            false, listOf(
            ), false
        ) { _, ctx ->
            val task = ctx.task
            if (task.def != NewTaskListener.taskDefNameMatInStoreA && task.def != NewTaskListener.taskDefNameMatInStoreB) return@TaskComponentDef
            val toSiteId = task.persistedVariables[PvsFields.ToSiteId]?.toString()
            if (toSiteId.isNullOrBlank()) throw BusinessError("无法获取当前任务的终点库位！")
            val bigTray = toSiteId.contains("Big")
            if (!PlcListener.checkToSiteAvailable(bigTray))
                throw BusinessError("库位【$toSiteId】的母托盘仍不能用，等待...")
            if (!CustomDataService.removeRecordIfAgvStopAudio(toSiteId))
                throw BusinessError("库位【$toSiteId】的母托盘状态还未进行人工确认，等待...")
        }
    )

    private fun getFromSiteInfo(task: RobotTask): FromSiteInfo {
        val from = task.persistedVariables[PvsFields.FromSiteId]?.toString()
        if (from.isNullOrBlank()) throw BusinessError("当前任务还未被指定起点，请求撤销此任务！")
        val fromGallery = from.substring(0, from.length - 2)
        val fromSiteIndex = from.last().toString().toInt()
        return FromSiteInfo(from, fromGallery, fromSiteIndex)
    }
}

data class BinResult(
    var layer: Int,
    var board: Boolean
)

data class FromSiteInfo(
    val siteId: String,
    val gallery: String,
    val index: Int
)

data class BinPoseInfos(
    var BinPoseInfo: List<BinPoseInfo>
)
